
#include "TwoWayList.cc"
#include "Record.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <iostream.h>

File :: File () {
}

File :: ~File () {
}

int File :: NextRec (Record &getMe) {

	// try to remove the record from the buffer
	if (myBuff.GetFirst (getMe)) {
		return 1;
	}

	// can't do it, so load the next page
	while (1) {

		// try to move to the next page
		if (curPos + 1 < curLength) {
			MovePointer (curPos);
		} else {
			return 0;
		}

		// now that we are at the next page, try
		// to return the next record
		if (myBuff.GetFirst (getMe)) {
			return 1;
		}
    }
}

void File :: MovePointer (int whichPage) {

	// this is done because the very first page does not have any data
	whichPage++;

	// see if someone wants to move past the end of the file
	if (whichPage > curLength) {
		cerr << "Error: you tried to move past the end of the file.\n";
		exit (1);
	}
	
	// write out the current page, if needed
	char *bits = new char[PAGESIZE];
	if (changed) {
		lseek (myFilDes, curPos * PAGESIZE, SEEK_SET);
		myBuff.ToBinary (bits);
		write (myFilDes, bits, PAGESIZE);
		changed = 0;
	}
	
	if (whichPage == curLength) {
		myBuff.EmptyItOut ();
		curLength = whichPage + 1;

	} else {

		// read the bits
		lseek (myFilDes, whichPage * PAGESIZE, SEEK_SET);
		read (myFilDes, bits, PAGESIZE);

		// and set up the buffer
		myBuff.FromBinary (bits);
	}
	
	delete [] bits;
        bits = 0;

	// set up the current page
	curPos = whichPage;
}

int File :: Append (Record &addMe) {

	// make sure that we are on the last page
	if (curPos != curLength - 1) {
		MovePointer (curLength - 2);
	}
	
	// try to add the record
	if (myBuff.Append (addMe)) {
		changed = 1;
		return curPos - 1;
	}

	// getting here means that we cannot fit the record, so first
	// write the current page to disk
	if (changed) {
		char *bits = new char[PAGESIZE];
		myBuff.ToBinary (bits);
		lseek (myFilDes, PAGESIZE * curPos, SEEK_SET);
		write (myFilDes, bits, PAGESIZE);
		delete [] bits;
	}

	// note that we have moved along
	curPos = curLength;
	curLength++;

	// then do the actual write; first empty out the buffer
	myBuff.EmptyItOut ();

	// and add the record
	if (myBuff.Append (addMe)) {
		changed = 1;
		return curPos - 1;
	}

	// error?
	cerr << "You tried to add a record that was too big for the page!\n";
	exit (1);

}

int File :: GetPage (Page &putItHere, int whichPage) {

	// this is because the first page has no data
	whichPage++;

	if (whichPage >= curLength) {
		cerr << "BAD: you tried to read past the end of the file\n";
		return 0;
	}

	// read in the specified page
	char *bits = new char[PAGESIZE];
	lseek (myFilDes, PAGESIZE * whichPage, SEEK_SET);
	read (myFilDes, bits, PAGESIZE);
	putItHere.FromBinary (bits);
	delete [] bits;
	
	return 1;
}

void File :: AddPage (Page &addMe, int whichPage) {

	// this is because the first page has no data
	whichPage++;

	// if we are trying to add past the end of the file, then
	// zero all of the pages out
	if (whichPage >= curLength) {
		
		// set the size
		curLength = whichPage + 1;	
	}

	// now write the page
	char *bits = new char[PAGESIZE];
	addMe.ToBinary (bits);
	lseek (myFilDes, PAGESIZE * whichPage, SEEK_SET);
	write (myFilDes, bits, PAGESIZE);
	delete [] bits;
}


void File :: Open (int fileLen, char *fName) {

	// the buffer is not dirty!
	changed = 0;

	// figure out the flags for the system open call
        int mode;
        if (fileLen == 0)
                mode = O_TRUNC | O_RDWR | O_CREAT;
        else
                mode = O_RDWR;

        printf("%s open len %d\n", fName, fileLen);

	// actually do the open
        myFilDes = open (fName, mode, S_IRUSR | S_IWUSR);

	// see if there was an error
	if (myFilDes < 0) {
		cerr << "BAD!  Open did not work.\n";
		exit (1);
	}

	// read in the buffer if needed
	if (fileLen != 0) {

		// read in the first few bits, which is the page size
		lseek (myFilDes, 0, SEEK_SET);
		read (myFilDes, &curLength, sizeof (int));
		cout << "Found " << curLength << " pages in the binary file.\n";

		// read in the first page
		char *bits = new char[PAGESIZE]; 
		lseek (myFilDes, PAGESIZE, SEEK_SET);
		read (myFilDes, bits, PAGESIZE);
		myBuff.FromBinary (bits);
		delete [] bits;
                bits = 0;


	// otherwise, empty it out
	} else {
		myBuff.EmptyItOut ();
		curLength = 2;
	}

	// and note that we are at the first pos!
	curPos = 1;
	changed = 0;

	// set up the path
	path = strdup (fName);
}

int File :: GetLength () {
	//fixed by fei in order to return the correct page number
	return curLength - 1 ;
}

int File :: Close () {

	// write out the buffer if needed
	if (changed) {
		char *bits = new char[PAGESIZE];
                lseek (myFilDes, curPos * PAGESIZE, SEEK_SET);
                myBuff.ToBinary (bits);
                write (myFilDes, bits, PAGESIZE);
               	delete [] bits; 
                bits = 0;
        }

	// write out the current length in pages
	lseek (myFilDes, 0, SEEK_SET);
	write (myFilDes, &curLength, sizeof (int));

	// close the file
	close (myFilDes);

	// and return the size
	return curLength;
	
}

